public class Main {
    public static void main(String[] args) {
        // 网络编程/ 网络原理
        // 网络背景知识
        // 这里的核心, Socket API (操作系统给应用程序提供的 网络编程 API)
        // Socket : 英文原义:是插座 , 而在这里叫做 "套接字"
        // (可以认为 socket api 是和传输层密切相关的. 传输层的两个最核心的协议 UDP 和 TCP)
        // (所以socket api 也提供了两种风格 UDP 和 TCP ,(准确来说是有三种风格的... unix域套接字 , 但是这个早就没有人用了))

        // **** 简单认识一下 UDP 和 TCP ****
        //  UDP: 无连接 不可靠传输 面向数据包 全双工
        //  TCP: 有连接 可靠传输  面向字节流  全双工
        // (打电话就是有连接的: 需要连接建立了才能通信.连接建立需要对方来"接收". 如果连接没建立好,通信不了;
        // 发短信/发微信 就是无连接的 . 这就不需要建立连接,可以直接发送)
        // (网络环境天然是复杂的. 不可能保证传输的数据 100% 就能到达!)
        // 什么叫做 "可靠传输", 指的是 发送方能知道自己的消息是不是发过去了,还是丢了.
        // 打电话其实也是可靠传输 , 发微信/发短信 也是不可靠传输 | 可靠不可靠和有连接无连接没有关系
        // 面向字节流: 数据传输就和 文件读写 类似,"流式"的
        // 面向数据报: 数据传输则以一个个的"数据报"为基本单位 (一个数据报可能有若干个字节,带有一定格式的)
        // 全双工: 一个通信通道 可以双向传输 , 既可以发送也可以接收
        // 半双工: 要么只发送 要么只接收, 这两个不能同时进行
        // (一根网线,里面其实是有8根线的...)

        // *** 基于 UDP 来编写一个简单的客户端服务器的网络通信程序...
        // ***** UDP数据报套接字编程 *****
        // java 提供了 两个类用来UDP 编程
        // DatagramSocket  和  DatagramPacket
        // DatagramSocket : 表示一个socket 对象. 在操作系统中 把这个 socket 对象也是当成一个文件来处理的, 也是相当于 文件描述符表 上的一夜
        // 普通文件,对应的硬件设备是 硬盘. socket 文件 对应的硬盘设备 是 网卡.
        // 一个 socket 对象, 就可以和另外一台主机进行通信了.
        // 如果 和 多个不同的主机通信, 则需要创建多个 socket 对象

        // DatagramSocket 是UDP Socket，用于发送和接收UDP数据报。
        // DatagramSocket 构造方法：
        // DatagramSocket() : 创建一个UDP数据报套接字的Socket，绑定到本机任意一个随机端口（一般用于客户端) (无参数版本, 系统会自动分配一个空闲的端口.(就是没端口号不行))
        // DatagramSocket(int port) :  创建一个UDP数据报套接字的Socket，绑定到本机指定的端口（一般用于服务端）(有参数版本,要传入一个端口号, 让当前的socket对象和指定的端口号关联起来)
        // (端口号 其实是一个 简单的整数), (本质上说, 不是 进程 和 端口号建立的联系, 而是 进程中的 socket对象 和 端口建立了联系)
        // DatagramSocket 方法：
        //  void receive(DatagramPacket p) : 从此套接字 接收 数据报（如果没有接收到数据报，该方法会阻塞等待） ,(此处传入的相当于是空的对象, receive方法内部,会对参数的这个空对象进行内容填充. 从而构造出结果数据了. 参数也是一个"输出型参数")
        // void send(DatagramPacket p) : 从此套接字 发送 数据报包（不会阻塞等待，直接发送）
        // void close() : 关闭 此数据报套接字 , 释放资源的

        // DatagramPacket 表示 UDP 中传输的一个报文 : 构造这个对象, 可以指定一些具体的数据进去
        //DatagramPacket是UDP Socket发送和接收的数据报。
        // DatagramPacket 构造方法：
        // DatagramPacket(byte[] buf, int length) : 构造一个DatagramPacket以用来接收数据报，接收的数据保存在字节数组（第一个参数buf）中，接收指定长度（第二个参数length）
        // (只是把 buf 缓冲区 给设置进去了)

        // DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) : 构造一个DatagramPacket以用来发送数据报，发送的数据为字节
        //数组（第一个参数buf）中，从0到指定长度（第二个参数length）。address指定目的主机的IP和端口号
        // (这个版本 即构造了一个缓冲区, 也构造了一个地址, SocketAddress 这个类表示 IP+port(端口号))

        // DatagramPacket 方法：
        // InetAddress getAddress() : 从接收的数据报中，获取发送端主机IP地址；或从发送的数据报中，获取接收端主机IP地址
        // int getPort(): 从接收的数据报中，获取发送端主机的端口号；或从发送的数据报中，获取接收端主机端口号
        // byte[] getData(): 获取数据报中的数据

        // 构造UDP发送的数据报时，需要传入 SocketAddress，该对象可以使用 InetSocketAddress 来创建

        // 上代码! (一个回显服务器(echo server))
        //  (一个普通的服务器: 收到请求, 根据请求计算响应, 返回响应)
        // (echo server , 省略了其中的"根据请求计算相应", 请求是啥,就返回啥, (这种代码没有什么用, 只是展示 socket api 基本用法))
        // 作为一个真正的服务器, 一定"根据请求计算响应" 这个环节是最重要的, 这个最严谨的业务逻辑

        // InetSocketAddress API

        // ***** TCP流套接字编程 ****
        // TCP 提供的API 主要是两个类. ServerSocket 和 Socket
        // ServerSocket 是专门给服务器使用的 Socket 对象
        // Socket 是 既会给 客户端使用 也会给 服务器使用
        // TCP 数据 不需要一个类来表示"TCP数据报", 因为TCP是面向字节流的,不是以数据报为传输单位, 属于是流式传输
        // ServerSocket API
        // ServerSocket 是创建TCP服务端Socket的API。
        // ServerSocket 构造方法： ServerSocket(int port) 创建一个服务端流套接字Socket，并绑定到指定端口
        // ServerSocket 方法：
        // Socket accept() 开始监听指定端口（创建时绑定的端口），有客户端连接后，返回一个服务端Socket对象，并基于该Socket建立与客户端的连接，否则阻塞等待
        // void close()  关闭此套接字

        // Socket API
        // Socket 是客户端Socket，或服务端中接收到客户端建立连接（accept方法）的请求后，返回的服务端Socket。
        // 在客户端边, 咱们代码里构造的.构造的时候指定一个 IP 和 端口号.(此处指定的IP 和 端口是服务器的 IP 和 端口). 有了这个信息,就能和服务器建立连接了
        // 不管是客户端还是服务端Socket，都是双方建立连接以后，保存的对端信息，及用来与对方收发数据的。

        // Socket 构造方法：
        // Socket(String host, int port) 创建一个客户端流套接字Socket，并与对应IP的主机上，对应端口的进程建立连接
        // Socket 方法：
        //  InetAddress getInetAddress()   返回套接字所连接的地址
        //  InputStream getInputStream()     返回此套接字的输入流  // 进一步通过Socket对象,获取到内部的 流对象, 借助流对象来进行发送/接收
        //  OutputStream getOutputStream()  返回此套接字的输出流

        //在 TCP 有连接的场景下, 针对连接这个概念, 有两种典型的表现方式
        // 1. 短链接: 客户端每次给服务器发信息, 先建立连接 , 发送请求, 读取响应, 关闭连接; 下次再发送, 则重新建立连接
        // 2. 长连接: 客户端建立连接之后 不着急断开连接, 然后再发送请求,读取响应; 再次发送请求, 读取响应;若干论之后,客户端确实短时间内不需要这个连接了了,此时再断开

        // TCP 诞生的初心 就是为了解决可靠传输的问题
    }
}